package com.bestvike.stone.core.util;

import com.bestvike.linq.exception.ArgumentOutOfRangeException;
import com.bestvike.stone.core.lang.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * Created by 许崇雷 on 2016-3-5.
 * 人民币金额大写转换器
 */
public final class RMBConverter {
    private static final String[] RMB_NUMBER = new String[]{"", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};      //数字
    private static final String[] RMB_POWER_THO = new String[]{"亿", "万"};                                                //四位权
    private static final String[] RMB_POWER_INT = new String[]{"仟", "佰", "拾", ""};                                      //整数权
    private static final String[] RMB_POWER_DEC = new String[]{"角", "分"};                                                //小数权
    private static final String RMB_MINUS = "负";   //符号
    private static final String RMB_UNIT = "元";    //单位
    private static final String RMB_ZERO = "零";    //零
    private static final String RMB_ALL = "整";     //整数结尾
    private static final char CHAR_MINUSONE = '/';  //特殊符号'-1'
    private static final char CHAR_ZERO = '0';      //特殊符号'0'
    private static final int NUM_MINUSONE = -1;     //整数-1
    private static final int NUM_ZERO = 0;          //整数0

    //构造函数
    private RMBConverter() {
    }

    /**
     * 金额转换为人民币.精确到分, 四舍六入五成双
     *
     * @param value 金额
     * @return 人民币
     */
    public static String toRMB(BigDecimal value) {
        return toRMB(value, 2, RoundingMode.HALF_EVEN);
    }

    /**
     * 金额转换为人民币.四舍六入五成双
     *
     * @param value    金额
     * @param decimals 小数位数,最多保留两位小数
     * @return 人民币
     */
    public static String toRMB(BigDecimal value, int decimals) {
        return toRMB(value, decimals, RoundingMode.HALF_EVEN);
    }

    /**
     * 金额转换为人民币
     *
     * @param value    金额
     * @param decimals 小数位数,最多保留两位小数
     * @param mode     舍入方式
     * @return 人民币
     */
    public static String toRMB(BigDecimal value, int decimals, RoundingMode mode) {
        return new RMB(value, decimals, mode).toString();
    }

    /**
     * 人民币
     */
    private static final class RMB {
        private BigDecimal value;//舍入后要转换为人民币的值

        /**
         * 构造函数
         *
         * @param value    金额
         * @param decimals 小数位数,最多保留两位小数
         * @param mode     舍入方式
         */
        RMB(BigDecimal value, int decimals, RoundingMode mode) {
            if (value.compareTo(BigDecimal.valueOf(Long.MIN_VALUE)) < 0 || value.compareTo(BigDecimal.valueOf(Long.MAX_VALUE)) > 0)
                throw new ArgumentOutOfRangeException("value");
            if (decimals > 2)
                throw new ArgumentOutOfRangeException("decimals");
            this.value = value.setScale(decimals, mode);
        }

        /**
         * 将人民币金额写到StringBuilder实例中
         *
         * @param builder StringBuilder实例
         * @return 传入的StringBuilder实例
         */
        public StringBuilder append(StringBuilder builder) {
            //符号
            BigDecimal value = this.value;
            if (value.compareTo(BigDecimal.ZERO) < 0) {
                builder.append(RMB_MINUS);
                value = value.negate();
            }
            //分拆
            long intPart = value.longValue();
            BigDecimal decPart = value.subtract(BigDecimal.valueOf(intPart));
            //转换
            if (decPart.compareTo(BigDecimal.ZERO) == 0)//0.0 或x.0
                new RMBInt(intPart).append(builder).append(RMB_ALL);//xx元整
            else if (intPart == NUM_ZERO)//0.x
                new RMBDec(decPart).append(builder);
            else//x.x
            {
                new RMBInt(intPart).append(builder);
                RMBDec rmbDec = new RMBDec(decPart);
                if (rmbDec.startsWithZero)
                    builder.append(RMB_ZERO);
                rmbDec.append(builder);
            }
            return builder;
        }

        /**
         * 转换为人民币金额
         *
         * @return 人民币金额
         */
        @Override
        public String toString() {
            return this.append(new StringBuilder(32)).toString();
        }
    }

    /**
     * 人民币整数部分
     */
    private static final class RMBInt {
        private long value;//整数部分数据
        private RMBThousand[] rmbThousands;//整数部分四位一组的数据

        /**
         * 构造函数
         *
         * @param value 整数部分
         */
        RMBInt(long value) {
            this.value = value;
            String _value = String.valueOf(value);
            int rowCount = (int) Math.ceil((double) _value.length() / 4d);//存储需要MoneyThousand个数
            char[] values = StringUtils.leftPad(_value, rowCount * 4, CHAR_MINUSONE).toCharArray();//左侧补CHAR-1
            this.rmbThousands = new RMBThousand[rowCount];
            for (int rowIndex = 0; rowIndex < rowCount; rowIndex++)
                this.rmbThousands[rowIndex] = new RMBThousand(values, rowIndex);
        }

        /**
         * 将人民币金额写到StringBuilder实例中
         *
         * @param builder StringBuilder实例
         * @return 传入的StringBuilder实例
         */
        public StringBuilder append(StringBuilder builder) {
            if (this.value == NUM_ZERO)
                return builder.append(RMB_ZERO).append(RMB_UNIT);
            boolean lastIsZero = false;//上一个是否为0
            for (RMBThousand thousand : this.rmbThousands) {
                //为0000
                if (thousand.isZero) {
                    lastIsZero = true;
                    continue;
                }
                //不为0000
                if (lastIsZero || thousand.startsWithZero)
                    builder.append(RMB_ZERO);
                thousand.append(builder);
                lastIsZero = false;
            }
            return builder.append(RMB_UNIT);
        }

        /**
         * 转换为人民币金额
         *
         * @return 人民币金额
         */
        @Override
        public String toString() {
            return this.append(new StringBuilder(16)).toString();
        }
    }

    /**
     * 人民币整数部分的四位
     */
    private static final class RMBThousand {
        private int[] value; //数据 两侧的0用-1代替,中间的0保留
        private int powerIndex;//权索引
        private boolean startsWithZero;//最高位是否以0开头
        private boolean isZero;//四位是否全部为0

        /**
         * 构造函数
         *
         * @param values   整数部分转换的字符数组,长度为4的倍数,长度不足在高位补-1
         * @param rowIndex 从最高位开始,所属的组索引
         */
        RMBThousand(char[] values, int rowIndex) {
            //初始化值
            this.value = new int[]{values[rowIndex * 4] - CHAR_ZERO, values[rowIndex * 4 + 1] - CHAR_ZERO, values[rowIndex * 4 + 2] - CHAR_ZERO, values[rowIndex * 4 + 3] - CHAR_ZERO};
            this.powerIndex = values.length / 4 - rowIndex - 1;
            this.startsWithZero = this.value[0] == NUM_ZERO;
            this.isZero = true;
            //去除高位0
            for (int i = 0; i < 4; i++) {
                if (this.value[i] > 0)
                    break;
                this.value[i] = NUM_MINUSONE;
            }
            //去除低位0
            for (int i = 3; i >= 0; i--) {
                if (this.value[i] > 0) {
                    this.isZero = false;
                    break;
                }
                this.value[i] = NUM_MINUSONE;
            }
        }

        /**
         * 将人民币金额写到StringBuilder实例中
         *
         * @param builder StringBuilder实例
         * @return 传入的StringBuilder实例
         */
        public StringBuilder append(StringBuilder builder) {
            //值
            int bit;
            for (int i = 0; i < 4; i++) {
                bit = this.value[i];
                if (bit < 0) {//两侧0
                } else if (bit == 0) {//中间0
                    if (!StringUtils.endsWith(builder, RMB_ZERO))
                        builder.append(RMB_ZERO);
                } else {//有值
                    builder.append(RMB_NUMBER[bit]).append(RMB_POWER_INT[i]);//N仟/佰/拾
                }
            }
            //权
            if (!this.isZero) {
                if (this.powerIndex % 2 == 1)
                    builder.append(RMB_POWER_THO[1]);//万
                for (int i = 0; i < this.powerIndex / 2; i++)//亿
                    builder.append(RMB_POWER_THO[0]);
            }
            return builder;
        }

        /**
         * 转换为人民币金额
         *
         * @return 人民币金额
         */
        @Override
        public String toString() {
            return this.append(new StringBuilder(8)).toString();
        }
    }

    /**
     * 人民币小数部分
     */
    private static final class RMBDec {
        private int[] value;//小数部分
        private boolean startsWithZero;//角是否以0开头

        /**
         * 构造函数
         *
         * @param value 小数部分
         */
        RMBDec(BigDecimal value) {
            String _value = String.valueOf(value.multiply(new BigDecimal("100")).intValue());
            String values = StringUtils.leftPad(_value, 2, CHAR_ZERO);
            this.value = new int[]{values.charAt(0) - CHAR_ZERO, values.charAt(1) - CHAR_ZERO};
            this.startsWithZero = this.value[0] == NUM_ZERO;
        }

        /**
         * 将人民币金额写到StringBuilder实例中
         *
         * @param builder StringBuilder实例
         * @return 传入的StringBuilder实例
         */
        public StringBuilder append(StringBuilder builder) {
            int bit;
            for (int i = 0; i < 2; i++) {
                bit = this.value[i];
                if (bit > 0)
                    builder.append(RMB_NUMBER[bit]).append(RMB_POWER_DEC[i]);
            }
            return builder;
        }

        /**
         * 转换为人民币金额
         *
         * @return 人民币金额
         */
        @Override
        public String toString() {
            return this.append(new StringBuilder(4)).toString();
        }
    }
}
